package com.tanger.im;

import com.alibaba.fastjson.JSONObject;
import com.tanger.common.socket.NoticePushUser;
import com.tanger.common.utils.BeanUtils;
import com.tanger.common.utils.JSON;
import com.tanger.common.utils.MD5Util;
import com.tanger.common.utils.StringUtil;
import com.tanger.im.entity.ImUserRecord;
import com.tanger.im.event.IMChatEvent;
import com.tanger.im.service.record.IMUserRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 聊天socket
 * @Author Hchenbin
 * @Date 2023/12/28 15:19
 * @Version 1.0
 */
@Slf4j
@Component
@ServerEndpoint(value = "/tanger/socket/{fId}/{mode}/{tId}")
public class IMUserWebSocket {

    /**
     * redis client
     */
    private static StringRedisTemplate redisTemplate;

    private static IMUserCaches iMUserCaches;

    private static IMUserRecordService recordService;

//    private static ApplicationEventPublisher publisher;

    private static final String FLAG = "OPEN";

    @Autowired
    public void initRedis(StringRedisTemplate redis) {
        IMUserWebSocket.redisTemplate = redis;
    }
    @Autowired
    public void initCaches(IMUserCaches caches) {
        IMUserWebSocket.iMUserCaches = caches;
    }
    @Autowired
    public void initRecordService(IMUserRecordService service) {
        IMUserWebSocket.recordService = service;
    }
//    @Autowired
//    public void initEvent(ApplicationEventPublisher eventPublisher) {
//        IMUserWebSocket.publisher = eventPublisher;
//    }

    /**
     * 创建
     * @param session
     * @param fId 发起对象ID
     * @param mode 模式 1.单聊 2.群聊
     * @param tId 接收对象ID
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("fId") Long fId,
                       @PathParam("mode") Integer mode, @PathParam("tId") Long tId) {
        if (!StringUtil.isNull(fId)){
            // 初始化缓存数据
            iMUserCaches.create(fId, session);

            List<ImUserRecord> records;
            String key = String.format(CacheKey.IM_USER_RECORD_LIST, fId);
            String chche = redisTemplate.opsForValue().get(key);
            if (StringUtil.isNull(chche)) {
                records = iMUserCaches.merge(fId,recordService.findAll(fId));
                redisTemplate.opsForValue().set(key, JSON.stringify(records));
            } else {
                records = JSONObject.parseArray(chche,ImUserRecord.class);
            }

            //合并返回聊天记录
            send(fId, null, FLAG, mode, records);
        }
    }

    /**
     * 发送消息
     * @param session
     * @param fId 发起对象ID
     * @param mode 模式 1.单聊 2.群聊
     * @param tId 接收对象ID
     * @param text 消息内容
     */
    @OnMessage
    public void onMessage(Session session, @PathParam("fId") Long fId,
                          @PathParam("mode") Integer mode, @PathParam("tId") Long tId, String text) {
        IMMessagePush node = JSON.parse(text,IMMessagePush.class);
        String code = code(mode, fId, tId);
        //更新最新聊天内容
        redisTemplate.opsForValue().set(String.format(CacheKey.IM_USER_NEW_RECORD, code), text);
        //异步处理
        //TODO 更新数据库记录
//        publisher.publishEvent(new IMChatEvent(tId, fId, tId, node));
        //更新聊天窗口
        ImUserRecord ur = ImUserRecord.builder()
                .fuId(fId).tuId(tId).offLineTotal(1).type(mode)
                .md5Key(MD5Util.encode(fId.toString()+tId.toString())+"_"+mode)
                .content(JSON.stringify(node)).lastTime(node.getSendTime())
                .build();
        List<ImUserRecord> records;
        String cacha = redisTemplate.opsForValue().get(String.format(CacheKey.IM_USER_RECORD, MD5Util.encode(fId.toString())));
        if (!StringUtil.isNull(cacha)) {
            records = JSONObject.parseArray(cacha, ImUserRecord.class);

            if (records.stream().anyMatch(r -> r.getTuId().equals(tId))) {
                records = records.stream()
                        .map(r -> {
                            if (ur.getTuId().equals(r.getTuId())) {
                                BeanUtils.copyProperties(ur, r);
                            }
                            return r;
                        }).collect(Collectors.toList());
            }
        } else {
            records = new ArrayList<>();
            records.add(ur);
        }
        redisTemplate.opsForValue()
                .set(String.format(CacheKey.IM_USER_RECORD, MD5Util.encode(fId.toString())),
                        JSON.stringify(records));

        //追加聊天内容
        List<IMMessagePush> pushs = new ArrayList<>();
        String initCacha = redisTemplate.opsForValue().get(String.format(CacheKey.IM_USER_INIT_RECORD, code));
        if (!StringUtil.isNull(initCacha)) {
            pushs = JSONObject.parseArray(initCacha, IMMessagePush.class);
        }
        pushs.add(node);
        redisTemplate.opsForValue().set(String.format(CacheKey.IM_USER_INIT_RECORD, code), JSON.stringify(pushs));
        send(fId, tId, null, mode, node);
    }

    /**
     * 关闭
     */
    @OnClose
    public void onClose(Session session, @PathParam("fId") Long fId) {
        // 移除缓存数据
        NoticePushUser user = iMUserCaches.remove(session);
        if (user.getUid() == null) return;
    }

    /**
     * 发生错误
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 推送消息
     * @param uId 接收用户ID
     * @param obj 消息内容
     * @param fId 发送用户
     */
    private void send(Long fId, Long uId, String flag, Integer mode, Object obj) {
        Session session;
        if (FLAG.equals(flag)) {
            session = iMUserCaches.getUser(fId);
        } else {
            session = iMUserCaches.getUser(uId);
        }
        String text = JSON.stringify(obj);
        log.info("[im-push] uId: {}, send message data: {}", uId, text);

        try {
            if (Objects.isNull(session)) {
                //保存离线信息
                iMUserCaches.saveOffLine(fId, uId, mode, JSON.parse(JSON.stringify(obj), IMMessagePush.class));
                return;
            }
            session.getBasicRemote().sendText(text);
        } catch (IOException e) {
            log.error("[im-push] send message error", e);
        }
    }

    private String code(Integer mode, Long fId, Long tId) {
        String code = "";
        switch (mode) {
            case 1 : code = MD5Util.encode(fId.toString()+tId.toString());
            break;
            case 2 : code = MD5Util.encode(tId.toString());
            break;
        }
        return code;
    }
}